﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Othello
{
    [Serializable]
    public struct BoardStruct
    {
        public UInt64 SelfRows;
        public UInt64 OtherRows;

        public BoardStruct(UInt64 selfRows, UInt64 otherRows) { SelfRows = selfRows; OtherRows = otherRows; }

        public override int GetHashCode()
        {
            return SelfRows.GetHashCode() + OtherRows.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj is BoardStruct)
            {
                BoardStruct board = (BoardStruct)obj;
                return board.SelfRows == SelfRows && board.OtherRows == OtherRows;
            }
            else
                return false;
        }
    }

    [Serializable]
    public class Board : ICloneable
    {
        public const int Size = 8;
        protected internal BoardStruct board;
        protected internal UInt64 moveableRows = 0;
        protected internal UInt64 otherMoveableRows = 0;
        private int moveableCount = 0;
        private int otherMoveableCount = 0;

        private int[] moveablePoints = new int[28];
        private bool finished = false;
        private bool reversed = false;

        public Board() { }

        public Board(bool initlize) { if (initlize)Initlize(); }

        public bool Reversed
        {
            get { return reversed; }
        }

        public UInt64 SelfRows { get { return board.SelfRows; } }

        public UInt64 OtherRows { get { return board.OtherRows; } }

        public bool Finished
        {
            get { return finished; }
        }

        public int MoveableCount
        {
            get { return moveableCount; }
        }

        public int OtherMoveableCount
        {
            get { return otherMoveableCount; }
        }

        public int SelfCount
        {
            get
            {
                return OthelloUtil.CountBit(SelfRows);
            }
        }

        public int OtherCount
        {
            get
            {
                return OthelloUtil.CountBit(OtherRows);
            }
        }

        public Board Clone()
        {
            Board clone = new Board();
            clone.CopyFrom(this);
            return clone;
        }

        object ICloneable.Clone()
        {
            return Clone();
        }

        public void CopyFrom(Board source)
        {
            if (source == null) return;
            board = source.board;
            moveableRows = source.moveableRows;
            moveableCount = source.moveableCount;
            otherMoveableRows = source.otherMoveableRows;
            otherMoveableCount = source.otherMoveableCount;
            Array.Copy(source.moveablePoints, moveablePoints, Size);
            finished = source.finished;
            reversed = source.reversed;
        }

        public void Initlize()
        {
            reversed = false;
            finished = false;
            board.SelfRows = 0x810000000L;
            board.OtherRows = 0x1008000000L;
            CalMoveablePlaces();
        }

        public Piece this[int row, int column]
        {
            get
            {
                if (((moveableRows >> (row * Size + column)) & 1) != 0) return Piece.Moveable;
                else if (((SelfRows >> (row * Size + column)) & 1) != 0) return Piece.Self;
                else if (((OtherRows >> (row * Size + column)) & 1) != 0) return Piece.Other;
                else return Piece.None;
            }
        }

        public int[] MoveablePoints
        {
            get { return moveablePoints; }
        }

        public void PutDown(int row, int column)
        {
            OthelloUtil.PutDown(ref board, row, column);
            Reverse();
        }

        private void CalMoveablePlaces()
        {
            moveableRows = OthelloUtil.GetMoveablePlaces(SelfRows, OtherRows);
            moveableCount = OthelloUtil.CountBit(moveableRows);
            otherMoveableRows = OthelloUtil.GetMoveablePlaces(OtherRows, SelfRows);
            otherMoveableCount = OthelloUtil.CountBit(otherMoveableRows);
            for (int i = 0, j = 0; i < Size * Size; i++)
            {
                if ((moveableRows >> i & 1) != 0) moveablePoints[j++] = i;
            }
            finished = moveableCount == 0 && otherMoveableCount == 0;
        }

        public Piece[,] Points
        {
            get
            {
                Piece[,] points = new Piece[8, 8];
                for (int i = 0; i < Size; i++)
                {
                    for (int j = 0; j < Size; j++)
                    {
                        points[i, j] = this[i, j];
                    }
                }
                return points;
            }
        }

        public void Reverse()
        {
            UInt64 t = board.SelfRows;
            board.SelfRows = board.OtherRows;
            board.OtherRows = t;
            CalMoveablePlaces();
            reversed = !reversed;
        }
    }

    public enum Piece
    {
        None = 0,
        Self = 1,
        Other = 2,
        Moveable = 3
    }
}
